<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Spring学习(3)AOP | 后端学习记录</title><meta name="keywords" content="Spring"><meta name="author" content="h0ss"><meta name="copyright" content="h0ss"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="接Spring学习(2)：https:&#x2F;&#x2F;h0ss.gitee.io&#x2F;2021&#x2F;07&#x2F;30&#x2F;Spring学习-2-IoC&#x2F; 4、AOP部分4.1 AOP概述在OOP的开发中，对于一些重复的操作可以抽离成模块，这可以减少代码量，但还是无法从根本上解决代码的冗余。在这种情况下我们可以把这些重复的操作抽离成切面，通过在运行时动态代理组合进原有的对象，这就是AOP，它是对OOP的补充。 AOP即面向切面编">
<meta property="og:type" content="article">
<meta property="og:title" content="Spring学习(3)AOP">
<meta property="og:url" content="https://blog.gpnusz.cn/2021/07/30/Spring%E5%AD%A6%E4%B9%A0-3-AOP/index.html">
<meta property="og:site_name" content="后端学习记录">
<meta property="og:description" content="接Spring学习(2)：https:&#x2F;&#x2F;h0ss.gitee.io&#x2F;2021&#x2F;07&#x2F;30&#x2F;Spring学习-2-IoC&#x2F; 4、AOP部分4.1 AOP概述在OOP的开发中，对于一些重复的操作可以抽离成模块，这可以减少代码量，但还是无法从根本上解决代码的冗余。在这种情况下我们可以把这些重复的操作抽离成切面，通过在运行时动态代理组合进原有的对象，这就是AOP，它是对OOP的补充。 AOP即面向切面编">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://www.hualigs.cn/image/610c2015e15b8.jpg">
<meta property="article:published_time" content="2021-07-29T17:36:08.000Z">
<meta property="article:modified_time" content="2022-07-28T07:11:30.562Z">
<meta property="article:author" content="h0ss">
<meta property="article:tag" content="Spring">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://www.hualigs.cn/image/610c2015e15b8.jpg"><link rel="shortcut icon" href="/img/favicon.png"><link rel="canonical" href="https://blog.gpnusz.cn/2021/07/30/Spring%E5%AD%A6%E4%B9%A0-3-AOP/"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css" media="print" onload="this.media='all'"><script>const GLOBAL_CONFIG = { 
  root: '/',
  algolia: undefined,
  localSearch: undefined,
  translate: undefined,
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":false},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '天',
  date_suffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: undefined,
  lightbox: 'fancybox',
  Snackbar: undefined,
  source: {
    jQuery: 'https://cdn.jsdelivr.net/npm/jquery@latest/dist/jquery.min.js',
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/justifiedGallery/dist/js/jquery.justifiedGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/justifiedGallery/dist/css/justifiedGallery.min.css'
    },
    fancybox: {
      js: 'https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@latest/dist/jquery.fancybox.min.js',
      css: 'https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@latest/dist/jquery.fancybox.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: false,
  isanchor: false
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = { 
  isPost: true,
  isHome: false,
  isHighlightShrink: false,
  isToc: false,
  postUpdate: '2022-07-28 15:11:30'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    })(window)</script><meta name="generator" content="Hexo 5.4.0"><link rel="stylesheet" href="/css/prism-tomorrow.css" type="text/css"></head><body><div id="web_bg"></div><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="author-avatar"><img class="avatar-img" src="/img/avatar.jpg" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="site-data"><div class="data-item is-center"><div class="data-item-link"><a href="/archives/"><div class="headline">文章</div><div class="length-num">51</div></a></div></div><div class="data-item is-center"><div class="data-item-link"><a href="/tags/"><div class="headline">标签</div><div class="length-num">14</div></a></div></div></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 时间轴</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="/link/"><i class="fa-fw fas fa-link"></i><span> 友链</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="not-top-img" id="page-header"><nav id="nav"><span id="blog_name"><a id="site-name" href="/">后端学习记录</a></span><div id="menus"><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 时间轴</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="/link/"><i class="fa-fw fas fa-link"></i><span> 友链</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div><div id="toggle-menu"><a class="site-page"><i class="fas fa-bars fa-fw"></i></a></div></div></nav></header><main class="layout" id="content-inner"><div id="post"><div id="post-info"><h1 class="post-title">Spring学习(3)AOP</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2021-07-29T17:36:08.000Z" title="发表于 2021-07-30 01:36:08">2021-07-30</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2022-07-28T07:11:30.562Z" title="更新于 2022-07-28 15:11:30">2022-07-28</time></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-wordcount"><i class="far fa-file-word fa-fw post-meta-icon"></i><span class="post-meta-label">字数总计:</span><span class="word-count">4.5k</span><span class="post-meta-separator">|</span><i class="far fa-clock fa-fw post-meta-icon"></i><span class="post-meta-label">阅读时长:</span><span>16分钟</span></span></div></div></div><article class="post-content" id="article-container"><p>接Spring学习(2)：<a target="_blank" rel="noopener" href="https://h0ss.gitee.io/2021/07/30/Spring%E5%AD%A6%E4%B9%A0-2-IoC/">https://h0ss.gitee.io/2021/07/30/Spring学习-2-IoC/</a></p>
<h3 id="4、AOP部分"><a href="#4、AOP部分" class="headerlink" title="4、AOP部分"></a>4、AOP部分</h3><h4 id="4-1-AOP概述"><a href="#4-1-AOP概述" class="headerlink" title="4.1 AOP概述"></a>4.1 AOP概述</h4><p>在OOP的开发中，对于一些重复的操作可以抽离成模块，这可以减少代码量，但还是无法从根本上解决代码的冗余。在这种情况下我们可以<strong>把这些重复的操作抽离成切面，通过在运行时动态代理组合进原有的对象</strong>，这就是AOP，它是对OOP的补充。</p>
<p><strong>AOP即面向切面编程</strong>，实际上就是对一些方法进行业务上面的按需增强，将一些与业务逻辑无关的业务方法（如：日志打印、权限校验、数据缓存等）抽离开来作为增强器，再利用动态代理进行增强，从这我们也可以体会到AOP也有实现解耦的作用，并且AOP 可以实现<strong>组件化、可插拔式</strong>的功能扩展。</p>
<p>AOP的设计原理是<strong>对原有业务逻辑的横切增强</strong>，底层是<strong>运行时动态代理机制</strong>。</p>
<p>不同于OOP以对象为关注的核心，<strong>AOP的核心是切面</strong>。</p>
<h4 id="4-2-术语"><a href="#4-2-术语" class="headerlink" title="4.2 术语"></a>4.2 术语</h4><p><strong>①目标对象</strong>：指的是被代理对象，也就是那个需要被增强的对象；</p>
<p><strong>②连接点</strong>：在Spring中，连接点指的是目标对象中的所有方法；</p>
<p><strong>③切入点</strong>：指的是对目标对象进行增强的连接点，目标对象中的连接点可能很多，但需要增强的可能不是全部，所以切入点一定是连接点，但连接点不一定是切入点；</p>
<p><strong>④通知</strong>：用来增强对象的那些代码（如：日志打印、记录等）；</p>
<p><strong>⑤代理对象</strong>：指的是目标对象和通知的组合</p>
<p><strong>⑥切面</strong>：指的是切入点和通知的组合</p>
<h4 id="4-3-通知类型"><a href="#4-3-通知类型" class="headerlink" title="4.3 通知类型"></a>4.3 通知类型</h4><p><strong>前置通知 Before：</strong>在目标方法(切入点方法)调用之前触发；</p>
<p><strong>后置通知 After：</strong>在目标方法(切入点方法)调用之后触发；</p>
<p><strong>返回通知 AfterReturing：</strong>在目标方法(切入点方法)成功返回之后触发；</p>
<p><strong>异常通知 AfterThrowing：</strong>在目标方法(切入点方法)出现/抛出异常之后触发；</p>
<p><strong>环绕通知 Around：</strong>它可以直接拿到目标对象，以及要执行的方法，所以可以在程序执行的任意位置进行切入。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span>&#123;</span><br><span class="line">	<span class="comment">//前置通知</span></span><br><span class="line">    Object res = pjp.proceed();</span><br><span class="line">    <span class="comment">//返回通知</span></span><br><span class="line">&#125;<span class="keyword">catch</span>(e)&#123;</span><br><span class="line">    <span class="comment">//异常通知</span></span><br><span class="line">&#125;<span class="keyword">finally</span>&#123;</span><br><span class="line">    <span class="comment">//后置通知</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h4 id="4-4-切入点表达式"><a href="#4-4-切入点表达式" class="headerlink" title="4.4 切入点表达式"></a>4.4 切入点表达式</h4><p>Spring中的AOP配置是根据切入点表达式去**找到特定的方法进行切入(增强)**，因此在实现AOP之前，我们需要了解切入点表达式的各种写法。</p>
<h5 id="1）切入点表达式的语法："><a href="#1）切入点表达式的语法：" class="headerlink" title="1）切入点表达式的语法："></a>1）切入点表达式的语法：</h5><p>execution(访问限定符  方法返回值类型   方法全类名(参数列表类型)   [throws] 异常全类名 )</p>
<h5 id="2）通配符"><a href="#2）通配符" class="headerlink" title="2）通配符"></a>2）通配符</h5><p>如果包名为 .. 则表示所有下级包(递归)，如果参数为 .. 则表示不限制参数，如果包名/方法名为*表明全部包/方法，同时表达式中支持|| &amp;&amp; 操作符</p>
<p>例如：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">①execution(<span class="keyword">public</span> <span class="keyword">int</span> top.jtszt.impl.MyCalculator.*(<span class="keyword">int</span>,<span class="keyword">int</span>))</span><br><span class="line">②execution(<span class="keyword">int</span> top.jtszt..*.*(..))</span><br></pre></td></tr></table></figure>

<p>①表示切入的是 <code>top.jtszt.impl.MyCalculator</code> 类下的所有 <code>返回值为int型</code> 且 <code>带有两个int型参数</code> 的 <code>公有</code> 的方法</p>
<p>②表示切入的是 <code>top.jtszt所有下级子包</code> 中的 <code>所有类</code> 下的所有<code>返回值为int型</code>的<code>公有</code>方法</p>
<h4 id="4-5-AOP实现（基于xml）"><a href="#4-5-AOP实现（基于xml）" class="headerlink" title="4.5 AOP实现（基于xml）"></a>4.5 AOP实现（基于xml）</h4><blockquote>
<p>背景：目标对象为top.jtszt.impl.MyCalculator，它是对top.jtszt.inter.Calculator接口的实现，其中有add、sub、div、multi这四个连接点，而切面类为top.jtszt.utils.LogUtils，其中有logStart、logReturn、logException、logEnd、logAround 这五个通知方法，现需要使用切面类对目标对象进行切入。</p>
</blockquote>
<p>首先需要在maven导入AOP所需的依赖，包括spring-aop(被spring-context依赖)、aopalliance、 aspectjweaver 、cglib。接着在spring的配置文件中声明AOP配置，这里需要导入aop名称空间。</p>
<p><strong>①切面类注入IoC</strong>：为切面类配置bean；</p>
<p><strong>②配置切入点表达式</strong>：接着配置aop使用的是<code>&lt;aop:config&gt;</code>标签，为了达到切入点表达式复用的效果，我们可以先使用<code> &lt;aop:pointcut&gt;</code>标签声明切入点，它的<code>expression属性</code>即是切入点表达式，在下面我们只需要根据其id就可以复用这个表达式了；<em>（注意：被切入的类必须注入IoC容器）</em></p>
<p><strong>③定义切面类</strong>：使用<code>&lt;aop:aspect&gt;</code>标签进行定义，<code>ref属性</code>指向的是切面类bean，接着在标签体内定义各种通知方法；</p>
<p><strong>④定义通知方法</strong>：有五个标签可以定义通知方法，在标签体内 <code>method</code> 属性为通知方法名，<code> pointcut-ref属性</code> 指向上面定义的切入点表达式。<code>&lt;aop:before&gt;</code>代表前置通知；<code>&lt;aop:after-returning&gt;</code>代表返回通知，可以使用returning属性定义接收return值的变量名，在切入方法中作为参数传入；<code>&lt;aop:after-throwing&gt;</code>代表异常通知，可以使用throwing属性定义接收异常信息的变量名，在切入方法中作为参数传入；<code>&lt;aop:after&gt;</code>代表后置通知；<code>&lt;aop:around&gt;</code>代表环绕通知。</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">beans</span>&gt;</span></span><br><span class="line">	<span class="comment">&lt;!-- 首先需要为切面类配置bean --&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;logUtils2&quot;</span> <span class="attr">class</span>=<span class="string">&quot;top.jtszt.utils.LogUtils&quot;</span>/&gt;</span></span><br><span class="line">     <span class="comment">&lt;!-- 在配置文件中配置AOP --&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">aop:config</span>&gt;</span></span><br><span class="line">         <span class="comment">&lt;!-- 定义切入点表达式 --&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">aop:pointcut</span> <span class="attr">id</span>=<span class="string">&quot;myPoint&quot;</span> <span class="attr">expression</span>=<span class="string">&quot;execution(public * top.jtszt.impl.MyCalculator.*(int,int))&quot;</span>/&gt;</span></span><br><span class="line">         <span class="comment">&lt;!-- 定义一个切面类 --&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">aop:aspect</span> <span class="attr">ref</span>=<span class="string">&quot;logUtils2&quot;</span>&gt;</span></span><br><span class="line">             <span class="comment">&lt;!--定义前置通知方法--&gt;</span></span><br><span class="line">             <span class="tag">&lt;<span class="name">aop:before</span> <span class="attr">method</span>=<span class="string">&quot;logStart&quot;</span> <span class="attr">pointcut-ref</span>=<span class="string">&quot;myPoint&quot;</span>/&gt;</span></span><br><span class="line">             <span class="comment">&lt;!--定义返回通知方法--&gt;</span></span><br><span class="line">             <span class="tag">&lt;<span class="name">aop:after-returning</span> <span class="attr">method</span>=<span class="string">&quot;logReturn&quot;</span> <span class="attr">pointcut-ref</span>=<span class="string">&quot;myPoint&quot;</span> <span class="attr">returning</span>=<span class="string">&quot;result&quot;</span>/&gt;</span></span><br><span class="line">             <span class="comment">&lt;!--定义异常通知方法--&gt;</span></span><br><span class="line">             <span class="tag">&lt;<span class="name">aop:after-throwing</span> <span class="attr">method</span>=<span class="string">&quot;logException&quot;</span> <span class="attr">pointcut-ref</span>=<span class="string">&quot;myPoint&quot;</span> <span class="attr">throwing</span>=<span class="string">&quot;exception&quot;</span>/&gt;</span></span><br><span class="line">             <span class="comment">&lt;!--定义后置通知方法--&gt;</span></span><br><span class="line">             <span class="tag">&lt;<span class="name">aop:after</span> <span class="attr">method</span>=<span class="string">&quot;logEnd&quot;</span> <span class="attr">pointcut-ref</span>=<span class="string">&quot;myPoint&quot;</span>/&gt;</span></span><br><span class="line">             <span class="comment">&lt;!--定义环绕通知方法--&gt;</span></span><br><span class="line">             <span class="tag">&lt;<span class="name">aop:around</span> <span class="attr">method</span>=<span class="string">&quot;logAround&quot;</span> <span class="attr">pointcut-ref</span>=<span class="string">&quot;myPoint&quot;</span> /&gt;</span></span><br><span class="line">         <span class="tag">&lt;/<span class="name">aop:aspect</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;/<span class="name">aop:config</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h4 id="4-6-AOP实现（基于注解）"><a href="#4-6-AOP实现（基于注解）" class="headerlink" title="4.6 AOP实现（基于注解）"></a>4.6 AOP实现（基于注解）</h4><p><strong>1）切面类注入IoC</strong>：为切面类加上@Component与@Aspect注解</p>
<p><strong>2）配置切入点表达式</strong>：在切面类中定义一个空方法，使用@Pointcut 注解声明切入点表达式，以便在下面复用这个表达式；</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Pointcut(&quot;execution(public int top.jtszt.impl.MyCalculator.*(int,int))&quot;)</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">pointcutExpression</span><span class="params">()</span></span>&#123;&#125;</span><br></pre></td></tr></table></figure>

<p><strong>3）定义通知方法</strong>： </p>
<p>**① @Before()**：表明是在方法开始前切入；</p>
<p>**② @AfterReturning()**：表明是在方法正常返回后切入，后可声明接收返回值的参数名；</p>
<p><strong>③ @AfterThrowing()</strong> ：表明是在方法抛出异常后切入，后可声明接收异常的参数名；</p>
<p><strong>④ @After()</strong> ：表明是在方法最终结束时切入(如try..catch中的finally)；</p>
<p><strong>⑤ @Around()</strong> ：表明这是一个环绕通知方法，环绕方法会先于其他四个通知方法执行，这个方法的返回值代表的就是调用实际方法的返回值。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Before(&quot;pointcutExpression()&quot;)</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">logStart</span><span class="params">()</span></span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@AfterReturning(value=&quot;pointcutExpression()&quot;,returning = &quot;result&quot;)</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">logReturn</span><span class="params">(Object result)</span></span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@AfterThrowing(value=&quot;pointcutExpression()&quot;,throwing = &quot;exception&quot;)</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">logException</span><span class="params">(Exception exception)</span></span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@After(&quot;pointcutExpression()&quot;)</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">logEnd</span><span class="params">()</span></span>&#123;&#125;</span><br></pre></td></tr></table></figure>

<p><strong>4）开启注解AOP</strong></p>
<p>如果使用xml+注解，可以在xml中配置<code>&lt;aop:aspectj-autoproxy/&gt;</code>开启注解aop。</p>
<p>如果使用纯注解，可以在配置类加上<code>@EnableAspectJAutoProxy</code>注解开启注解aop 。</p>
<h4 id="4-7-通知方法参数"><a href="#4-7-通知方法参数" class="headerlink" title="4.7 通知方法参数"></a>4.7 通知方法参数</h4><p>像在使用原生的动态代理一样，如果需要在通知方法中获取切入方法的参数与方法名等信息，需要传入JoinPoint类型的参数。其中有几个比较常用的方法：</p>
<ul>
<li>Object  JoinPoint.getTarget()：获取未代理的目标对象</li>
<li>Object  JoinPoint.getThis()：获取代理对象</li>
<li>Object[]  JoinPoint.getArgs()： 获取切入方法的参数列表</li>
<li>Signature  JoinPoint.getSignature()：获取方法签名</li>
<li>String  Signature.getName()： 获取方法名</li>
<li>Method  (MethodSignature)Signature.getMethod()：获取方法信息</li>
</ul>
<p>需要注意的是，由于环绕通知方法的返回值代表的就是调用实际方法的返回值，因此其中需要传入一个<code>ProceedingJoinPoint</code>类型的参数，通过这个对象调用<code>proceed()</code>方法可以得到实际方法的返回值，这个语句也相当于动态代理中调用<code>invoke()</code>方法。</p>
<h4 id="4-8-多切面执行顺序"><a href="#4-8-多切面执行顺序" class="headerlink" title="4.8 多切面执行顺序"></a>4.8 多切面执行顺序</h4><p>如果有多个切面类对同一个方法进行切入，遵循从外到内的规则（按切面类名的 unicode 编码的十六进制顺序执行）。</p>
<p>如：外层切面类A为AspectOne，内层切面类B为AspectTwo。</p>
<p>执行顺序为： A前置通知方法→B前置通知方法→实际方法→B返回/异常通知方法→B后置通知方法→A返回/异常通知方法→A后置通知方法</p>
<p>如果想改变切面的执行顺序，可以通过@Order注解设置切面类优先级，传入一个int型参数，数值越小优先值越高，默认为最低优先级。</p>
<p>此外，同切面中的相同类型通知方法的执行顺序也是按照unicode编码顺序来。</p>
<h4 id="4-9-用AOP做事务控制"><a href="#4-9-用AOP做事务控制" class="headerlink" title="4.9 用AOP做事务控制"></a>4.9 用AOP做事务控制</h4><blockquote>
<p>背景信息：书店进行图书销售活动，并且会员在系统中存有余额信息，在用户购买图书之后系统需要减图书库存同时减用户余额，这是一个整体(一个事务)。现在需要用AOP做事务控制，保证两个操作的一致性。</p>
</blockquote>
<blockquote>
<p>流程：让Spring管理数据库连接池以及jdbcTemplate，DAO利用自动装配的jdbcTemplate进行数据库操作，Service做具体的结账方法；之后让Spring利用AOP对这个结账方法做事务控制。</p>
</blockquote>
<h5 id="1）环境准备"><a href="#1）环境准备" class="headerlink" title="1）环境准备"></a>1）环境准备</h5><p>①添加maven依赖，包括mysql-connector-java、spring-tx、c3p0、spring-jdbc以及ioc、aop相关的依赖；</p>
<p>②准备数据库表</p>
<p>用户信息表：</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> t_account(</span><br><span class="line">	username <span class="type">VARCHAR</span>(<span class="number">50</span>) <span class="keyword">PRIMARY</span> KEY,</span><br><span class="line">	balance <span class="type">INT</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure>

<p>图书信息表：</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> t_book(</span><br><span class="line">	isbn <span class="type">VARCHAR</span>(<span class="number">50</span>) <span class="keyword">PRIMARY</span> KEY,</span><br><span class="line">	book_name <span class="type">VARCHAR</span>(<span class="number">50</span>),</span><br><span class="line">	price <span class="type">INT</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure>

<p>图书库存表：</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> t_book_stock(</span><br><span class="line">	isbn <span class="type">VARCHAR</span>(<span class="number">50</span>),</span><br><span class="line">	stock <span class="type">INT</span>,</span><br><span class="line">	<span class="keyword">CONSTRAINT</span> fk_isbn <span class="keyword">FOREIGN</span> KEY(isbn) <span class="keyword">REFERENCES</span> t_book(isbn)</span><br><span class="line">)</span><br></pre></td></tr></table></figure>

<p>操作数据库：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Repository</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BookDAO</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    JdbcTemplate jdbcTemplate;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//减少余额的方法</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">updateBalance</span><span class="params">(String userName, <span class="keyword">int</span> price)</span></span>&#123;</span><br><span class="line">        String sql = <span class="string">&quot;UPDATE t_account SET balance=balance-? WHERE username=?&quot;</span>;</span><br><span class="line">        jdbcTemplate.update(sql,price,userName);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 获取图书价格的方法</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getPrice</span><span class="params">(String isbn)</span></span>&#123;</span><br><span class="line">        String sql = <span class="string">&quot;SELECT price FROM t_book WHERE isbn=?&quot;</span>;</span><br><span class="line">        <span class="keyword">return</span> jdbcTemplate.queryForObject(sql, Integer.class, isbn);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 减库存的方法</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">updateStock</span><span class="params">(String isbn)</span></span>&#123;</span><br><span class="line">        String sql = <span class="string">&quot;UPDATE t_book_stock SET stock=stock-1 WHERE isbn=?&quot;</span>;</span><br><span class="line">        jdbcTemplate.update(sql,isbn);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>服务方法（为方便不写接口）：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BookService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> BookDAO bookDAO;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">checkout</span><span class="params">(String username,String isbn)</span></span>&#123;</span><br><span class="line">        <span class="comment">//减库存</span></span><br><span class="line">        bookDAO.updateStock(isbn);</span><br><span class="line">        <span class="comment">//减余额</span></span><br><span class="line">        bookDAO.updateBalance(username, bookDAO.getPrice(isbn));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>xml中还有包扫描等操作这里就不贴了。</p>
<h5 id="2）配置声明式事务（基于xml）"><a href="#2）配置声明式事务（基于xml）" class="headerlink" title="2）配置声明式事务（基于xml）"></a>2）配置声明式事务（基于xml）</h5><p>上面的Service方法是没有做事务管理的，一旦减库存方法执行完毕之后出现异常，那么该库存将被成功减去1，但是用户余额却没扣除，这显然是不行的。接着我们要对它进行事务的管理，首先是基于xml的配置，它依赖于tx和aop名称空间。</p>
<p>首先需要配置数据源，并且由于上文使用了jdbcTemplate自动装配，这里顺便配置它。</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">beans</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 配置写在db.properties中 --&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">context:property-placeholder</span> <span class="attr">location</span>=<span class="string">&quot;db.properties&quot;</span>/&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- c3p0连接池 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;ds&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.mchange.v2.c3p0.ComboPooledDataSource&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;user&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.user&#125;&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;password&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.password&#125;&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;jdbcUrl&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.jdbcUrl&#125;&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;driverClass&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.driverClass&#125;&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;maxPoolSize&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.maxPoolSize&#125;&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;minPoolSize&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.minPoolSize&#125;&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line">	<span class="comment">&lt;!-- 配置jdbcTemplate --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;jdbcTemplate&quot;</span> <span class="attr">class</span>=<span class="string">&quot;org.springframework.jdbc.core.JdbcTemplate&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;dataSource&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;ds&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>接着配置Spring提供的事务管理器，当使用JDBC/MyBatis进行持久化时可以使用DataSourceTransactionManager做事务管理器。</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;tm&quot;</span> <span class="attr">class</span>=<span class="string">&quot;org.springframework.jdbc.datasource.DataSourceTransactionManager&quot;</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;dataSource&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;ds&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>接着需要告诉Spring哪些方法是事务方法，使用的是tx名称空间下的<code>advice</code>标签，其中<code>transaction-manager</code>指向事务管理器，该标签下的<code>attributes</code>只有一个标签<code>method</code>，<code>name</code>属性用于匹配事务方法(可以使用通配符)，此外还有其他的一些属性：</p>
<ul>
<li><p>timeout 设置超时自动终止事务并回滚；</p>
</li>
<li><p>read-only 设置事务为只读；</p>
</li>
<li><p>no-rollback-for 指定哪些异常不回滚，传入异常全类名，默认为空；</p>
</li>
<li><p>rollback-for 当方法触发异常时回滚，传入异常全类名；默认是捕捉所有运行时异常和错误；</p>
</li>
<li><p>isolation 修改事务的隔离级别；</p>
</li>
<li><p>propagation 指定事务的传播行为；</p>
</li>
</ul>
<p>这些属性也可以在@Transactional 注解中配置。</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">tx:advice</span> <span class="attr">id</span>=<span class="string">&quot;myAdvice&quot;</span> <span class="attr">transaction-manager</span>=<span class="string">&quot;tm&quot;</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 指明哪些方法是事务方法--&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">tx:attributes</span>&gt;</span></span><br><span class="line">		<span class="tag">&lt;<span class="name">tx:method</span> <span class="attr">name</span>=<span class="string">&quot;*&quot;</span>/&gt;</span></span><br><span class="line">		<span class="tag">&lt;<span class="name">tx:method</span> <span class="attr">name</span>=<span class="string">&quot;checkout&quot;</span> <span class="attr">timeout</span>=<span class="string">&quot;-1&quot;</span> <span class="attr">read-only</span>=<span class="string">&quot;false&quot;</span>/&gt;</span></span><br><span class="line">		<span class="tag">&lt;<span class="name">tx:method</span> <span class="attr">name</span>=<span class="string">&quot;get*&quot;</span> <span class="attr">read-only</span>=<span class="string">&quot;true&quot;</span>/&gt;</span></span><br><span class="line">	<span class="tag">&lt;/<span class="name">tx:attributes</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">tx:advice</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>上面只是声明事务方法，但实际上还需要设置切入点才能进行事务管理，只有成功切入了才有后面的事务管理。也就是说事务方法一定是切入点，但切入点不一定是事务方法。</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">aop:config</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">aop:pointcut</span> <span class="attr">id</span>=<span class="string">&quot;txPoint&quot;</span> <span class="attr">expression</span>=<span class="string">&quot;execution(* top.jtszt.*.*.*(..))&quot;</span>/&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- advice-ref：指向事务管理器的配置 --&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">aop:advisor</span> <span class="attr">advice-ref</span>=<span class="string">&quot;myAdvice&quot;</span> <span class="attr">pointcut-ref</span>=<span class="string">&quot;txPoint&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">aop:config</span>&gt;</span></span><br></pre></td></tr></table></figure>



<h5 id="3）配置声明式事务（基于注解）"><a href="#3）配置声明式事务（基于注解）" class="headerlink" title="3）配置声明式事务（基于注解）"></a>3）配置声明式事务（基于注解）</h5><p>首先需要在配置类加上<code>@EnableTransactionManagement</code> 表示开启事务管理器，也可以在xml文件中开启基于注解的声明式事务。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableTransactionManagement</span></span><br><span class="line"><span class="meta">@ComponentScan(&quot;top.jtszt&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ConfClass</span> </span>&#123;&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">tx:annotation-driven</span> <span class="attr">transaction-manager</span>=<span class="string">&quot;tm&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure>

<p>之后在配置类中配置上数据源与事务管理器</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableTransactionManagement</span></span><br><span class="line"><span class="meta">@ComponentScan(&quot;top.jtszt&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ConfClass</span> </span>&#123;</span><br><span class="line">    <span class="comment">//读配置文件</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Properties <span class="title">properties</span><span class="params">()</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        Properties properties = <span class="keyword">new</span> Properties();</span><br><span class="line">        properties.load(<span class="keyword">new</span> FileReader(<span class="string">&quot;db.properties&quot;</span>));</span><br><span class="line">        <span class="keyword">return</span> properties;</span><br><span class="line">    &#125;</span><br><span class="line">	<span class="comment">//配置数据源</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> ComboPooledDataSource <span class="title">dataSource</span><span class="params">(Properties properties)</span> <span class="keyword">throws</span> PropertyVetoException </span>&#123;</span><br><span class="line">        ComboPooledDataSource ds = <span class="keyword">new</span> ComboPooledDataSource();</span><br><span class="line">        ds.setUser(properties.getProperty(<span class="string">&quot;jdbc.user&quot;</span>));</span><br><span class="line">        ds.setPassword(properties.getProperty(<span class="string">&quot;jdbc.password&quot;</span>));</span><br><span class="line">        ds.setJdbcUrl(properties.getProperty(<span class="string">&quot;jdbc.jdbcUrl&quot;</span>));</span><br><span class="line">        ds.setDriverClass(properties.getProperty(<span class="string">&quot;jdbc.driverClass&quot;</span>));</span><br><span class="line">        <span class="keyword">return</span> ds;</span><br><span class="line">    &#125;</span><br><span class="line">	<span class="comment">//配置jdbcTemplate</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> JdbcTemplate <span class="title">jdbcTemplate</span><span class="params">(DataSource dataSource)</span></span>&#123;</span><br><span class="line">        JdbcTemplate jdbcTemplate = <span class="keyword">new</span> JdbcTemplate();</span><br><span class="line">        jdbcTemplate.setDataSource(dataSource);</span><br><span class="line">        <span class="keyword">return</span> jdbcTemplate;</span><br><span class="line">    &#125;</span><br><span class="line">	<span class="comment">//配置事务管理器</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> DataSourceTransactionManager <span class="title">dataSourceTransactionManager</span><span class="params">(DataSource dataSource)</span></span>&#123;</span><br><span class="line">        DataSourceTransactionManager tm = <span class="keyword">new</span> DataSourceTransactionManager();</span><br><span class="line">        tm.setDataSource(dataSource);</span><br><span class="line">        <span class="keyword">return</span> tm;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>接着还需要告诉Spring哪些方法是事务方法，使用的是<code>@Transactional</code>，并且最好设置rollbackFor属性，这个注解还有其他的属性可以设置，与<code>&lt;tx:method&gt;</code>相似。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Transactional(rollbackFor = &#123;Exception.class&#125;)</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">checkout</span><span class="params">(String username,String isbn)</span></span>&#123;...&#125;</span><br></pre></td></tr></table></figure>

<p>此外<code>@Transactional</code>还可以设置在类上，表示全部方法都是事务方法。</p>
<h5 id="4）事务的传播行为"><a href="#4）事务的传播行为" class="headerlink" title="4）事务的传播行为"></a>4）事务的传播行为</h5><p>上面讲到了事务方法设置中有一个属性可以设置事务的传播行为，那么事务的传播行为是啥？</p>
<p>事务传播行为指的是一个事务方法被另一个事务方法调用时运行的方式。Spring中定义了其中传播行为，分别是：</p>
<ul>
<li>REQUIRED：如果当前有事务则在其中运行，否则新开一个事务，在自己的事务里运行 （事务的属性都继承于大事务）；</li>
<li>REQUIRES_NEW： 当前方法必须开启新事务，并在自己的事务里运行，如果有事务正在运行则挂起；</li>
<li>SUPPORTS：如果有事务在运行则方法在这个事务中运行，否则可以不运行在事务中；</li>
<li>NOT_SUPPORTED：当前方法不应运行在事务中，如果有运行的事务则将其挂起；</li>
<li>MANDATORY：当前方法必须运行在事务内部，否则抛出异常；</li>
<li>NEVER：当前方法不应运行在事务内部，否则抛出异常；</li>
<li>NESTED： 如果有事务在运行则当前方法应该在这个事务的嵌套事务中运行，否则启动一个新事务，并在自己的事务中运行。</li>
</ul>
<h5 id="5）事务失效"><a href="#5）事务失效" class="headerlink" title="5）事务失效"></a>5）事务失效</h5><p>一般情况下，事务失效会有如下场景：</p>
<ul>
<li>在SSM开发中Spring和SpringMVC是分管两个容器，这时如果SpringMVC扫描了@Service那么对于@controller注入的则是没有事务的方法，这会导致事务失效。因此声明式事务的配置必须由Spring容器加载。</li>
<li>如果@Transactional注解标注在接口上，但实现类使用 Cglib 代理，则事务会失效。你标注的是接口，但是Cglib代理时直接拿到实现类去构建代理对象，也就绕过了接口的事务管理。</li>
<li>事务默认捕捉RuntimeException，如果抛出Exception，默认不捕捉，事务失效，所以一般情况下都是显式声明捕捉Exception。</li>
<li>在Service 方法中自行 try-catch 异常处理，那么呈现给事务拦截器的就是没有异常的情况，自然也会导致事务失效。</li>
<li>同一个类中，一个方法调用了自身另一个带有事务控制的方法，直接调用时也会导致事务失效。</li>
</ul>
<hr>
<p><em>参考资料：</em></p>
<ul>
<li><a target="_blank" rel="noopener" href="https://docs.spring.io/spring-framework/docs/5.1.3.RELEASE/spring-framework-reference/">Spring Framework 5.1.3.RELEASE文档</a></li>
<li><a target="_blank" rel="noopener" href="https://juejin.cn/book/6857911863016390663/section">从 0 开始深入学习 Spring-掘金小册</a></li>
<li><a target="_blank" rel="noopener" href="https://snailclimb.gitee.io/javaguide-interview/#/./docs/e-1spring">JavaGuide-Spring</a></li>
<li><a target="_blank" rel="noopener" href="https://blog.csdn.net/vipshop_fin_dev/article/details/109017732">Spring中单例Bean的线程安全问题-CSDN</a></li>
<li><a target="_blank" rel="noopener" href="https://www.cnblogs.com/zrtqsk/p/3735273.html">Spring Bean的生命周期-博客园</a></li>
<li><a target="_blank" rel="noopener" href="https://javadoop.com/post/spring-ioc">Spring IOC 容器源码分析_Javadoop</a></li>
<li><a target="_blank" rel="noopener" href="https://blog.csdn.net/lj1314ailj/article/details/80118372">Spring5 系统架构-CSDN</a></li>
<li><a target="_blank" rel="noopener" href="https://www.bilibili.com/video/BV1d4411g7tv?p=256">雷丰阳Spring、Spring MVC、MyBatis课程-bilibili</a></li>
</ul>
</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="mailto:undefined">h0ss</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="https://blog.gpnusz.cn/2021/07/30/Spring%E5%AD%A6%E4%B9%A0-3-AOP/">https://blog.gpnusz.cn/2021/07/30/Spring%E5%AD%A6%E4%B9%A0-3-AOP/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="https://blog.gpnusz.cn" target="_blank">后端学习记录</a>！</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/tags/Spring/">Spring</a></div><div class="post_share"></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/2021/08/01/SpringMVC%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"><img class="prev-cover" src="https://static01.imgkr.com/temp/07cc3f2f859f40ef8733044657da8465.jpg" onerror="onerror=null;src='/img/404.jpg'" alt="cover of previous post"><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">SpringMVC学习笔记</div></div></a></div><div class="next-post pull-right"><a href="/2021/07/30/Spring%E5%AD%A6%E4%B9%A0-2-IoC/"><img class="next-cover" src="https://www.hualigs.cn/image/610c2015e15b8.jpg" onerror="onerror=null;src='/img/404.jpg'" alt="cover of next post"><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">Spring学习(2)IoC</div></div></a></div></nav><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span> 相关推荐</span></div><div class="relatedPosts-list"><div><a href="/2022/04/13/【解决方案】细说分片上传与极速秒传-SpringBoot-Vue/" title="【解决方案】细说分片上传与极速秒传(SpringBoot+Vue)"><img class="cover" src="https://i.loli.net/2021/09/01/KHSB3kcFZDQx4vo.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-04-13</div><div class="title">【解决方案】细说分片上传与极速秒传(SpringBoot+Vue)</div></div></a></div><div><a href="/2021/08/01/SpringMVC学习笔记/" title="SpringMVC学习笔记"><img class="cover" src="https://static01.imgkr.com/temp/07cc3f2f859f40ef8733044657da8465.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2021-08-01</div><div class="title">SpringMVC学习笔记</div></div></a></div><div><a href="/2021/07/30/Spring学习-2-IoC/" title="Spring学习(2)IoC"><img class="cover" src="https://www.hualigs.cn/image/610c2015e15b8.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2021-07-30</div><div class="title">Spring学习(2)IoC</div></div></a></div><div><a href="/2021/07/30/Spring学习-1-快速上手/" title="Spring学习(1)快速上手"><img class="cover" src="https://www.hualigs.cn/image/610c2015e15b8.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2021-07-30</div><div class="title">Spring学习(1)快速上手</div></div></a></div></div></div></div><div class="aside-content" id="aside-content"><div class="card-widget card-info"><div class="card-info-avatar is-center"><img class="avatar-img" src="/img/avatar.jpg" onerror="this.onerror=null;this.src='/img/friend_404.gif'" alt="avatar"/><div class="author-info__name">h0ss</div><div class="author-info__description">一个苦逼后端仔的学习记录</div></div><div class="card-info-data"><div class="card-info-data-item is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">51</div></a></div><div class="card-info-data-item is-center"><a href="/tags/"><div class="headline">标签</div><div class="length-num">14</div></a></div></div><a class="button--animated" id="card-info-btn" target="_blank" rel="noopener" href="https://gitee.com/h0ss"><i class="fab fa-github"></i><span>Gitee仓库</span></a></div><div class="card-widget card-announcement"><div class="item-headline"><i class="fas fa-bullhorn card-announcement-animation"></i><span>公告</span></div><div class="announcement_content">Java后端技术栈语雀文档：https://www.yuque.com/oliver-wdtb1/java</div></div><div class="sticky_layout"><div class="card-widget card-recent-post"><div class="item-headline"><i class="fas fa-history"></i><span>最新文章</span></div><div class="aside-list"><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/2022/11/16/%E7%BB%99%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E6%8F%90%E4%BA%86%E4%B8%AAPR/" title="给开源项目提了个PR">给开源项目提了个PR</a><time datetime="2022-11-15T17:28:40.000Z" title="发表于 2022-11-16 01:28:40">2022-11-16</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/2022/09/03/%E3%80%90MySQL%E3%80%91InnoDB%E7%9A%84%E8%A1%8C%E6%A0%BC%E5%BC%8F/" title="【MySQL】InnoDB的行格式">【MySQL】InnoDB的行格式</a><time datetime="2022-09-02T18:17:38.000Z" title="发表于 2022-09-03 02:17:38">2022-09-03</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/2022/08/21/%E3%80%90Debug%E3%80%91bitField-%E5%BC%95%E5%8F%91%E7%9A%84%E6%A0%88%E6%BA%A2%E5%87%BA%E6%8E%92%E9%94%99%E8%AE%B0/" title="【Debug】bitField 引发的栈溢出排错记">【Debug】bitField 引发的栈溢出排错记</a><time datetime="2022-08-20T18:10:40.000Z" title="发表于 2022-08-21 02:10:40">2022-08-21</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/2022/07/16/%E3%80%90RPC%E3%80%91%E8%AF%A6%E8%A7%A3SPI%E6%9C%BA%E5%88%B6/" title="【RPC】详解SPI机制">【RPC】详解SPI机制</a><time datetime="2022-07-15T18:51:57.000Z" title="发表于 2022-07-16 02:51:57">2022-07-16</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/2022/07/14/%E3%80%90Redis%E3%80%91%E9%87%8D%E8%A6%81%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%9F%BA%E7%A1%80/" title="【Redis】重要数据结构基础">【Redis】重要数据结构基础</a><time datetime="2022-07-13T17:22:08.000Z" title="发表于 2022-07-14 01:22:08">2022-07-14</time></div></div></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button id="go-up" type="button" title="回到顶部"><i class="fas fa-arrow-up"></i></button></div></div><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><div class="js-pjax"></div><script defer="defer" id="fluttering_ribbon" mobile="false" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/canvas-fluttering-ribbon.min.js"></script><script id="canvas_nest" defer="defer" color="0,0,255" opacity="0.7" zIndex="-1" count="99" mobile="false" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/canvas-nest.min.js"></script><script id="click-heart" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/click-heart.min.js" async="async" mobile="false"></script><script id="click-show-text" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/click-show-text.min.js" data-mobile="false" data-text="爱国,敬业,诚信,友善,自由,平等,公正,法治,富强,民主,文明,和谐" data-fontsize="16px" data-random="false" async="async"></script></div></body></html>